import { Injectable } from '@nestjs/common';
import { MatchService } from '../match.service';
import { MatchInfo } from '../entities/match.entity';
import { Group } from 'src/modules/group/entities/group.entity';
import { GroupPlayerScore as GroupPlayerScoreEntity } from 'src/modules/group-player-score/entities/group-player-score.entity';
import logger from 'src/common/utils/log';

const initGroupPlayerScore = {
  winMatchCount: 0,
  loseMatchCount: 0,
  winGameCount: 0,
  loseGameCount: 0,
  winRallyCount: 0,
  loseRallyCount: 0,
  eliminationPosition: 0,
};

class GroupPlayerScore extends GroupPlayerScoreEntity {
  defeat?: Array<number>; // 击败对手的ID
}

@Injectable()
export class MatchScoreService {
  constructor(private readonly matchService: MatchService) {}

  getMatchScore(
    group: Group,
    type: 'single' | 'team',
  ): Array<GroupPlayerScore> {
    logger.info(
      `start getMatchScore type: ${type} groupId: ${group.id} matchInfos: ${group.matchInfos.length}`,
    );
    const playerScoreInfo = group.matchInfos.reduce(
      (total, match): { [playerId: number]: GroupPlayerScore } => {
        const getPlayerScoreApi =
          type === 'single'
            ? this.getMatchPlayerScore
            : this.getTeamMatchPlayerScore;
        const [homePlayerScoreInfo, awayPlayerScoreInfo] =
          getPlayerScoreApi.call(this, match as MatchInfo, group.id);
        const homePlayerScoreInfoId = homePlayerScoreInfo.playerId;
        total[homePlayerScoreInfoId] = total[homePlayerScoreInfoId] || {};
        this.addObject(total[homePlayerScoreInfoId], homePlayerScoreInfo);
        const awayPlayerScoreInfoId = awayPlayerScoreInfo.playerId;
        total[awayPlayerScoreInfoId] = total[awayPlayerScoreInfoId] || {};
        this.addObject(total[awayPlayerScoreInfoId], awayPlayerScoreInfo);
        return total;
      },
      {},
    );
    logger.info(
      `end getMatchScore type: ${type} groupId: ${group.id} playerScoreInfo: ${Object.values(playerScoreInfo).length}`,
    );
    const sortedPlayerScore = this.sortPlayerScore(
      Object.values(playerScoreInfo),
    );
    return [...sortedPlayerScore].map((playerScore, index) => {
      playerScore.ranking = index + 1;
      return playerScore;
    });
  }

  getMatchPlayerScore(
    matchInfo: MatchInfo,
    groupId: number,
  ): [GroupPlayerScore, GroupPlayerScore] {
    const {
      winnerId,
      homePlayerId,
      awayPlayerId,
      games,
      homePlayer,
      awayPlayer,
      subMatchId,
    } = matchInfo;
    if (!winnerId) {
      return [
        {
          ...initGroupPlayerScore,
          playerId: homePlayerId,
          groupId: groupId,
          player: homePlayer,
        },
        {
          ...initGroupPlayerScore,
          playerId: awayPlayerId,
          groupId: groupId,
          player: awayPlayer,
        },
      ];
    }
    const isSubMatch = subMatchId !== undefined;
    let homePlayerEliminationPosition = null;
    let awayPlayerEliminationPosition = null;
    const isHomePlayerWin = winnerId === homePlayerId;
    const isAwayPlayerWin = winnerId === awayPlayerId;
    if (
      matchInfo.rangeMax &&
      matchInfo.rangeMin &&
      matchInfo.rangeMax - matchInfo.rangeMin === 1
    ) {
      homePlayerEliminationPosition = isHomePlayerWin
        ? matchInfo.rangeMin
        : matchInfo.rangeMax;
      awayPlayerEliminationPosition = isAwayPlayerWin
        ? matchInfo.rangeMin
        : matchInfo.rangeMax;
    }

    const homePlayerScoreInfo: GroupPlayerScore = {
      playerId: homePlayerId,
      player: homePlayer,
      groupId: groupId,
      // registrationIndex: matchInfo.homePlayer.registrationIndex,
      winMatchCount: homePlayerId === winnerId ? 1 : 0,
      loseMatchCount: homePlayerId !== winnerId ? 1 : 0,
      winGameCount: games.filter((game) => game.winnerId === homePlayerId)
        .length,
      loseGameCount: games.filter((game) => game.winnerId !== homePlayerId)
        .length,
      winRallyCount: games.reduce((acc, game) => {
        return acc + game.homePlayerRally;
      }, 0),
      loseRallyCount: games.reduce((acc, game) => {
        return acc + game.awayPlayerRally;
      }, 0),
      eliminationPosition: homePlayerEliminationPosition,
      defeat: isHomePlayerWin && [awayPlayerId],
      winTeamMatchCount: isHomePlayerWin && isSubMatch ? 1 : 0,
      loseTeamMatchCount: isHomePlayerWin && isSubMatch ? 0 : 1,
    };
    const awayPlayerScoreInfo: GroupPlayerScore = {
      playerId: awayPlayerId,
      player: awayPlayer,
      groupId: groupId,
      // registrationIndex: matchInfo.awayPlayer.registrationIndex,
      winMatchCount: awayPlayerId === winnerId ? 1 : 0,
      loseMatchCount: awayPlayerId !== winnerId ? 1 : 0,
      winGameCount: games.filter((game) => game.winnerId === awayPlayerId)
        .length,
      loseGameCount: games.filter((game) => game.winnerId !== awayPlayerId)
        .length,
      winRallyCount: games.reduce((acc, game) => {
        return acc + game.awayPlayerRally;
      }, 0),
      loseRallyCount: games.reduce((acc, game) => {
        return acc + game.homePlayerRally;
      }, 0),
      eliminationPosition: awayPlayerEliminationPosition,
      defeat: isAwayPlayerWin && [homePlayerId],
      winTeamMatchCount: isAwayPlayerWin && isSubMatch ? 1 : 0,
      loseTeamMatchCount: isAwayPlayerWin && isSubMatch ? 0 : 1,
    };

    return [homePlayerScoreInfo, awayPlayerScoreInfo];
  }
  addObject(obj1 = {}, obj2 = {}) {
    return Object.keys(obj2).reduce((acc, key) => {
      if (
        [
          'winTeamMatchCount',
          'loseTeamMatchCount',
          'winMatchCount',
          'loseMatchCount',
          'winGameCount',
          'loseGameCount',
          'winRallyCount',
          'loseRallyCount',
        ].includes(key)
      ) {
        acc[key] = (obj1[key] || 0) + (obj2[key] || 0);
      } else if (key === 'defeat') {
        // 如果是defeat 字段，需要合并数组，记录该人击败的所有人
        acc[key] = [...(obj1[key] || []), ...(obj2[key] || [])];
      } else {
        acc[key] = obj2[key];
      }
      return acc;
    }, obj1);
  }
  getTeamMatchPlayerScore(
    matchInfo: MatchInfo,
    groupId: number,
  ): [GroupPlayerScore, GroupPlayerScore] {
    const {
      winnerId,
      subMatchInfos,
      homePlayerId,
      awayPlayerId,
      homePlayer,
      awayPlayer,
    } = matchInfo;
    if (!winnerId) {
      return [
        { ...initGroupPlayerScore, playerId: homePlayerId, groupId: groupId },
        { ...initGroupPlayerScore, playerId: awayPlayerId, groupId: groupId },
      ];
    }
    const [subMatchHomeScoreInfo, subMatchAwayScoreInfo] = subMatchInfos.reduce(
      (total, subMatchInfo) => {
        const _winnerId =
          subMatchInfo.winnerId === subMatchInfo.homePlayerId // 这里计算当场胜者所在团队的id
            ? homePlayerId
            : awayPlayerId;
        const [homePlayerScoreInfo, awayPlayerScoreInfo] =
          this.getMatchPlayerScore(
            // 这里为了方便复用，将具体player的名称换成的所在队的名称
            {
              ...subMatchInfo,
              homePlayer,
              awayPlayer,
              homePlayerId,
              awayPlayerId,
              winnerId: _winnerId,
            } as any,
            groupId,
          );
        return [
          this.addObject(total[0], homePlayerScoreInfo),
          this.addObject(total[1], awayPlayerScoreInfo),
        ];
      },
      [{}, {}],
    );
    return [subMatchHomeScoreInfo as any, subMatchAwayScoreInfo as any];
  }
  sortPlayerScore(groupPlayerScores: GroupPlayerScore[]) {
    return groupPlayerScores.sort((a, b) => {
      // const res =
      //   !b.player || b.player.isByePlayer
      //     ? -1 // 轮空或者没有指定选手，往后排
      //     : (b.eliminationPosition || 0) - (a.eliminationPosition || 0) > 0 ||
      //         b.winMatchCount - a.winMatchCount > 0 ||
      //         b.winGameCount - a.winGameCount > 0 ||
      //         b.winRallyCount -
      //           b.loseRallyCount -
      //           (a.winRallyCount - a.loseRallyCount) >
      //           0 || // 计算净得分
      //         b.defeat?.includes(a.playerId)
      //       ? 1
      //       : -1; // 计算b是否击败过a
      if (!b.player || b.player.isByePlayer) {
        return -1;
      } else if (!a.player || a.player.isByePlayer) {
        return 1;
      }
      if (b.winTeamMatchCount - a.winTeamMatchCount > 0) {
        // 比较团赛胜局
        return 1;
      } else if (b.winTeamMatchCount - a.winTeamMatchCount < 0) {
        return -1;
      }
      if (b.winMatchCount - a.winMatchCount > 0) {
        // 比较胜场
        return 1;
      } else if (b.winMatchCount - a.winMatchCount < 0) {
        return -1;
      }
      if (b.winGameCount - a.winGameCount > 0) {
        // 比较胜局
        return 1;
      } else if (b.winGameCount - a.winGameCount < 0) {
        return -1;
      }
      if (
        // 比较净胜分
        b.winRallyCount -
          b.loseRallyCount -
          (a.winRallyCount - a.loseRallyCount) >
        0
      ) {
        // 比较胜 rally
        return 1;
      } else if (
        b.winRallyCount -
          b.loseRallyCount -
          (a.winRallyCount - a.loseRallyCount) <
        0
      ) {
        return -1;
      }
      if (b.defeat?.includes(a.playerId)) {
        return 1;
      } else {
        return 0;
      }
    });
  }
}
